package com.simen.hellobytes.http;

import com.simen.hellobytes.StringUtil;

import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpHeaderUtil {

    private static final String PATTERN_CONTENT_DISPOSITION_ATTACHMENT = "^attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\s*$";
    private static final String PATTERN_CONTENT_DISPOSITION_FILE_FORMDATA = "^form-data;\\s*name\\s*=\\s*(\"?)([^\"]*)(\"?);\\s*filename\\s*=\\s*(\"?)([^\"]*)(\"?)\\s*$";
    private static final String PATTERN_CONTENT_DISPOSITION_FORMDATA1 = "^form-data;\\s*name\\s*=\\s*(\"?)([^\"]*)(\"?);([\\s\\S]*)$";
    private static final String PATTERN_CONTENT_DISPOSITION_FORMDATA2 = "^form-data;\\s*name\\s*=\\s*(\"?)([^\"]*)(\"?)$";
    private static final String PATTERN_CONTENT_TYPE = "^([1-9a-zA-Z]+/[1-9a-zA-Z.-]+);[\\w\\W\\s]*$";

    /**
     * Parse the Content-Disposition HTTP Header. The format of the header
     * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
     * This header provides a filename for content that is going to be
     * downloaded to the file system. We only support the attachment type.
     * Note that RFC 2616 specifies the filename value must be double-quoted.
     * Unfortunately some servers do not quote the value so to maintain
     * consistent behaviour with other browsers, we allow unquoted values too.
     */
    public static String parseFileNameFromContentDisposition(String contentDisposition) {
        String value = parse(contentDisposition, PATTERN_CONTENT_DISPOSITION_ATTACHMENT, 2);
        if (StringUtil.isEmpty(value)) {
            return parse(contentDisposition, PATTERN_CONTENT_DISPOSITION_FILE_FORMDATA, 5);
        }
        return value;
    }

    public static String parseNameFromContentDisposition(String contentDisposition) {
        String value = parse(contentDisposition, PATTERN_CONTENT_DISPOSITION_FORMDATA1, 2);
        if (StringUtil.isEmpty(value)) {
            return parse(contentDisposition, PATTERN_CONTENT_DISPOSITION_FORMDATA2, 2);
        }
        return value;
    }

    private static String parse(String content, String patternStr, int group) {
        try {
            Pattern pattern = Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);
            Matcher m = pattern.matcher(content);
            if (m.find()) {
                return m.group(group);
            }
        } catch (IllegalStateException ex) {
            // This function is defined as returning null when it can't parse the header
        }
        return null;
    }

    public static String parseContentType(String contentType) {
        if (StringUtil.isTrimEmpty(contentType)) {
            return null;
        }

        if (contentType.contains(";")) {
            return parse(contentType, PATTERN_CONTENT_TYPE, 1);
        }

        return contentType.trim();
    }

    public static String parseCharsetFromContentType(String contentType) {
        if (StringUtil.isTrimEmpty(contentType)) {
            return null;
        }

        String lowCase = contentType.toLowerCase().replace(" ", "");
        int index = lowCase.indexOf("charset");
        StringBuilder builder = null;
        boolean foundEqual = false;
        for (int i = index + 7; i < lowCase.length(); i++) {
            char c = lowCase.charAt(i);
            if (c == '=') {
                foundEqual = true;
            } else {
                if (i == index + 7) {
                    //第一个字符必须是'='
                    return null;
                }
                if (foundEqual) {
                    if (builder == null) {
                        builder = new StringBuilder();
                    }
                    builder.append(c);
                }
            }
        }

        return builder == null ? null : builder.toString();
    }

    public static String getFirstValueFromHeadMap(Map<String, List<String>> headerMap, String headerName) {
        List<String> values = findHeaderFromMap(headerMap, headerName);
        if (values != null && !values.isEmpty()) {
            return values.get(0);
        }

        return null;
    }

    public static List<String> findHeaderFromMap(Map<String, List<String>> headerMap, String headerName) {
        Iterator<Map.Entry<String, List<String>>> iterator = headerMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<String>> entry = iterator.next();
            //header 值不区分大小 https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
            if (headerName.equalsIgnoreCase(entry.getKey())) {
                return entry.getValue();
            }
        }

        return null;
    }

    public static IdentityHashMap<String, String> convertListMapToIdentityHashMap(Map<String, List<String>> listMap) {
        IdentityHashMap<String, String> identityHashMap = new IdentityHashMap<String, String>();

        Iterator<Map.Entry<String, List<String>>> iterator = listMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<String>> entry = iterator.next();
            List<String> values = entry.getValue();

            for (int i = 0; values != null && i < values.size(); i++) {
                identityHashMap.put(entry.getKey(), values.get(i));
            }
        }

        return identityHashMap;
    }

    public static String getHeaderValue(String header) {
        int index = header.indexOf(":");
        if (index == -1) {
            return null;
        }

        if (index == header.length() - 1) {
            return "";
        }

        return header.substring(index + 1);
    }
}
